﻿using System.Collections.Generic;

namespace NodeBasedSkillEditor
{
    /// <summary>
    /// 调用了ForewardExecute，Update，BackwardExecute接口会返回的下一步行为
    /// </summary>
    public enum EDagExeState
    {
        Foreward,   // 向前调用下一个节点ForewardExecute
        Update,     // 调用当前节点的UpdateExecute
        Backward    // 回退调用上一个节点BackwardExecute
    }

    public class DAG_Executor
    {
        public RT_Node StartNode { get; private set; }
        public RT_Node CurrentRuningNode { get; private set; }
        public bool IsFinished { get; private set; } = false;
        public EDagExeState ExecuteState { get; private set; }
        public RT_Node NextNode { get; private set; }
        public RT_Node CurrentNode { get { return _executedStack.Count > 0 ? _executedStack.Peek() : null; } }
        public bool Finished { get { return CurrentNode == null; } }
        /// <summary>当前帧，1基</summary>
        public int CurrentFrame { get; private set; }

        Stack<RT_Node> _executedStack = new Stack<RT_Node>();

        /// <summary>
        /// 节点的执行信息不需要专门删除，在每次回收再利用时会清空，或者被丢弃时整体会被释放
        /// </summary>
        Dictionary<RT_Node, RT_NodeExeInfo> _nodeExeInfos = new Dictionary<RT_Node, RT_NodeExeInfo>();

        public DAG_Executor(RT_Node start)
        {
            StartNode = start;
        }

        public void StartExecute()
        {
            if (StartNode != null)
            {
                _executedStack.Clear();
                _nodeExeInfos.Clear();

                _executedStack.Push(StartNode);
                ExecuteState = EDagExeState.Foreward;

                CurrentFrame = 1;
                (ExecuteState, NextNode) = StartNode.ForewardExecute(this);
                TryKeepExecution();
            }
        }

        public void Update()
        {
            if (ExecuteState == EDagExeState.Update && !Finished)
            {
                CurrentFrame++;
                (ExecuteState, NextNode) = CurrentNode.UpdateExecute(this);
                TryKeepExecution();
            }
        }

        void TryKeepExecution()
        {
            while (!Finished)
            {
                switch (ExecuteState)
                {
                    case EDagExeState.Foreward:
                        if (NextNode != null)
                        {
                            _executedStack.Push(NextNode);
                            (ExecuteState, NextNode) = NextNode.ForewardExecute(this);
                        }
                        else
                        {
                            ExecuteState = EDagExeState.Backward;
                        }
                        break;

                    case EDagExeState.Backward:
                        (ExecuteState, NextNode) = CurrentNode.BackwardExecute(this);
                        if (ExecuteState == EDagExeState.Backward)
                        {
                            _executedStack.Pop();
                        }
                        break;

                    case EDagExeState.Update:
                    default:
                        return;
                }
            }
        }

        public RT_NodeExeInfo GetExeInfoOfNode(RT_Node node)
        {
            RT_NodeExeInfo info;
            _nodeExeInfos.TryGetValue(node, out info);
            return info;
        }

        public void SetExeInfoOfNode(RT_Node node, RT_NodeExeInfo info)
        {
            _nodeExeInfos[node] = info;
        }

        void ForewardExecuteNode(RT_Node node)
        {
            _executedStack.Push(node);
            CurrentRuningNode = node;

            var (state, toNode) = node.ForewardExecute(this);
            ProcessAfterExecuteNode(state, toNode);
        }

        void UpdateExecuteNode(RT_Node node)
        {
            var (state, toNode) = node.UpdateExecute(this);
            ProcessAfterExecuteNode(state, toNode);
        }

        /// <summary>
        /// 反向执行是特殊的流程，如果执行结果是再次前进或更新当前节点，那么返回的toNode是有意义的
        /// 如果执行结果是继续反向，先pop栈顶的历史，再取新的栈顶作为当前执行节点，以回溯执行历史栈
        /// </summary>
        /// <param name="node"></param>
        void BackwardExecuteNode(RT_Node node)
        {
            var (state, toNode) = node.BackwardExecute(this);
            if (state == EDagExeState.Backward)
            {
                _executedStack.Pop();
                if (_executedStack.Count == 0)
                {
                    CurrentRuningNode = null;
                    IsFinished = true;
                    return;
                }
                else
                {
                    CurrentRuningNode = _executedStack.Peek();
                }
            }
            ProcessAfterExecuteNode(state, toNode);
        }

        void ProcessAfterExecuteNode(EDagExeState state, RT_Node toNode)
        {
            ExecuteState = state;
            if (ExecuteState == EDagExeState.Foreward)
            {
                ForewardExecuteNode(toNode);
            }
            else if (ExecuteState == EDagExeState.Update)
            {
                CurrentRuningNode = toNode;
            }
            else if (ExecuteState == EDagExeState.Backward)
            {
                BackwardExecuteNode(CurrentRuningNode);
            }
        }

    }
}
